/*
 * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.service.fmgcode.gcode.dbconfig;



import com.service.fmgcode.gcode.common.*;
import com.service.fmgcode.gcode.dbconfig.po.TableInfo;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

import javax.persistence.Column;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import static java.util.stream.Collectors.toList;

/**
 * <p>
 * 实体类反射表辅助类
 * </p>
 *
 * @author hubin sjy
 * @since 2016-09-09
 */
public class TableInfoHelper {

    private static final Log logger = LogFactory.getLog(TableInfoHelper.class);

    /**
     * 储存反射类表信息
     */
    private static final Map<Class<?>, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<>();

    /**
     * 默认表主键名称
     */
    private static final String DEFAULT_ID_NAME = "id";

    /**
     * <p>
     * 获取实体映射表信息
     * </p>
     *
     * @param clazz 反射实体类
     * @return 数据库表反射信息
     */
    public static TableInfo getTableInfo(Class<?> clazz) {
        if (clazz == null
            || ReflectionKit.isPrimitiveOrWrapper(clazz)
            || clazz == String.class) {
            return null;
        }
        // https://github.com/baomidou/mybatis-plus/issues/299
        TableInfo tableInfo = TABLE_INFO_CACHE.get(ClassUtils.getUserClass(clazz));
        if (null != tableInfo) {
            return tableInfo;
        }
        //尝试获取父类缓存
        Class<?> currentClass = clazz;
        while (null == tableInfo && Object.class != currentClass) {
            currentClass = currentClass.getSuperclass();
            tableInfo = TABLE_INFO_CACHE.get(ClassUtils.getUserClass(currentClass));
        }
        if (tableInfo != null) {
            TABLE_INFO_CACHE.put(ClassUtils.getUserClass(clazz), tableInfo);
        }
        return tableInfo;
    }

    /**
     * <p>
     * 获取所有实体映射表信息
     * </p>
     *
     * @return 数据库表反射信息集合
     */
    @SuppressWarnings("unused")
    public static List<TableInfo> getTableInfos() {
        return new ArrayList<>(TABLE_INFO_CACHE.values());
    }

//    /**
//     * <p>
//     * 实体类反射获取表信息【初始化】
//     * </p>
//     *
//     * @param clazz 反射实体类
//     * @return 数据库表反射信息
//     */
//    public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
//        TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz);
//        if (tableInfo != null) {
//            if (builderAssistant != null) {
//                tableInfo.setConfiguration(builderAssistant.getConfiguration());
//            }
//            return tableInfo;
//        }
//
//        /* 没有获取到缓存信息,则初始化 */
//        tableInfo = new TableInfo(clazz);
//        GlobalConfig globalConfig;
//        if (null != builderAssistant) {
//            tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());
//            tableInfo.setConfiguration(builderAssistant.getConfiguration());
//            globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());
//        } else {
//            // 兼容测试场景
//            globalConfig = GlobalConfigUtils.defaults();
//        }
//
//        /* 初始化表名相关 */
//        final String[] excludeProperty = initTableName(clazz, globalConfig, tableInfo);
//
//        List<String> excludePropertyList = excludeProperty != null && excludeProperty.length > 0 ? Arrays.asList(excludeProperty) : Collections.emptyList();
//
//        /* 初始化字段相关 */
//        initTableFields(clazz, globalConfig, tableInfo, excludePropertyList);
//
//        /* 放入缓存 */
//        TABLE_INFO_CACHE.put(clazz, tableInfo);
//
//        /* 缓存 lambda */
//        LambdaUtils.installCache(tableInfo);
//
//        /* 自动构建 resultMap */
//        tableInfo.initResultMapIfNeed();
//
//        return tableInfo;
//    }
//
//    /**
//     * <p>
//     * 初始化 表数据库类型,表名,resultMap
//     * </p>
//     *
//     * @param clazz        实体类
//     * @param globalConfig 全局配置
//     * @param tableInfo    数据库表反射信息
//     * @return 需要排除的字段名
//     */
//    private static String[] initTableName(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {
//        /* 数据库全局配置 */
//        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
//        TableName table = clazz.getAnnotation(TableName.class);
//
//        String tableName = clazz.getSimpleName();
//        String tablePrefix = dbConfig.getTablePrefix();
//        String schema = dbConfig.getSchema();
//        boolean tablePrefixEffect = true;
//        String[] excludeProperty = null;
//
//        if (table != null) {
//            if (StringUtils.isNotBlank(table.value())) {
//                tableName = table.value();
//                if (StringUtils.isNotBlank(tablePrefix) && !table.keepGlobalPrefix()) {
//                    tablePrefixEffect = false;
//                }
//            } else {
//                tableName = initTableNameWithDbConfig(tableName, dbConfig);
//            }
//            if (StringUtils.isNotBlank(table.schema())) {
//                schema = table.schema();
//            }
//            /* 表结果集映射 */
//            if (StringUtils.isNotBlank(table.resultMap())) {
//                tableInfo.setResultMap(table.resultMap());
//            }
//            tableInfo.setAutoInitResultMap(table.autoResultMap());
//            excludeProperty = table.excludeProperty();
//        } else {
//            tableName = initTableNameWithDbConfig(tableName, dbConfig);
//        }
//
//        String targetTableName = tableName;
//        if (StringUtils.isNotBlank(tablePrefix) && tablePrefixEffect) {
//            targetTableName = tablePrefix + targetTableName;
//        }
//        if (StringUtils.isNotBlank(schema)) {
//            targetTableName = schema + StringPool.DOT + targetTableName;
//        }
//
//        tableInfo.setTableName(targetTableName);
//
//        /* 开启了自定义 KEY 生成器 */
//        if (null != dbConfig.getKeyGenerator()) {
//            tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));
//        }
//        return excludeProperty;
//    }

//    /**
//     * 根据 DbConfig 初始化 表名
//     *
//     * @param className 类名
//     * @param dbConfig  DbConfig
//     * @return 表名
//     */
//    private static String initTableNameWithDbConfig(String className, GlobalConfig.DbConfig dbConfig) {
//        String tableName = className;
//        // 开启表名下划线申明
//        if (dbConfig.isTableUnderline()) {
//            tableName = StringUtils.camelToUnderline(tableName);
//        }
//        // 大写命名判断
//        if (dbConfig.isCapitalMode()) {
//            tableName = tableName.toUpperCase();
//        } else {
//            // 首字母小写
//            tableName = StringUtils.firstToLowerCase(tableName);
//        }
//        return tableName;
//    }

//    /**
//     * <p>
//     * 初始化 表主键,表字段
//     * </p>
//     *
//     * @param clazz        实体类
//     * @param globalConfig 全局配置
//     * @param tableInfo    数据库表反射信息
//     */
//    public static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo, List<String> excludeProperty) {
//        /* 数据库全局配置 */
//        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
//        ReflectorFactory reflectorFactory = tableInfo.getConfiguration().getReflectorFactory();
//        //TODO @咩咩 有空一起来撸完这反射模块.
//        Reflector reflector = reflectorFactory.findForClass(clazz);
//        List<Field> list = getAllFields(clazz);
//        // 标记是否读取到主键
//        boolean isReadPK = false;
//        // 是否存在 @TableId 注解
//        boolean existTableId = isExistTableId(list);
//
//        List<TableFieldInfo> fieldList = new ArrayList<>();
//        for (Field field : list) {
//            if (excludeProperty.contains(field.getName())) {
//                continue;
//            }
//
//            /* 主键ID 初始化 */
//            if (existTableId) {
//                TableId tableId = field.getAnnotation(TableId.class);
//                if (tableId != null) {
//                    if (isReadPK) {
//                        throw ExceptionUtils.mpe("@TableId can't more than one in Class: \"%s\".", clazz.getName());
//                    } else {
//                        isReadPK = initTableIdWithAnnotation(dbConfig, tableInfo, field, tableId, reflector);
//                        continue;
//                    }
//                }
//            } else if (!isReadPK) {
//                isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, reflector);
//                if (isReadPK) {
//                    continue;
//                }
//            }
//
//            /* 有 @TableField 注解的字段初始化 */
//            if (initTableFieldWithAnnotation(dbConfig, tableInfo, fieldList, field)) {
//                continue;
//            }
//
//            /* 无 @TableField 注解的字段初始化 */
//            fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field));
//        }
//
//        /* 检查逻辑删除字段只能有最多一个 */
//        Assert.isTrue(fieldList.parallelStream().filter(TableFieldInfo::isLogicDelete).count() < 2L,
//            String.format("@TableLogic can't more than one in Class: \"%s\".", clazz.getName()));
//
//        /* 字段列表,不可变集合 */
//        tableInfo.setFieldList(Collections.unmodifiableList(fieldList));
//
//        /* 未发现主键注解，提示警告信息 */
//        if (!isReadPK) {
//            logger.warn(String.format("Can not find table primary key in Class: \"%s\".", clazz.getName()));
//        }
//    }

//    /**
//     * <p>
//     * 判断主键注解是否存在
//     * </p>
//     *
//     * @param list 字段列表
//     * @return true 为存在 @TableId 注解;
//     */
//    public static boolean isExistTableId(List<Field> list) {
//        return list.stream().anyMatch(field -> field.isAnnotationPresent(TableId.class));
//    }

//    /**
//     * <p>
//     * 主键属性初始化
//     * </p>
//     *
//     * @param dbConfig  全局配置信息
//     * @param tableInfo 表信息
//     * @param field     字段
//     * @param tableId   注解
//     * @param reflector Reflector
//     */
//    private static boolean initTableIdWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
//                                                     Field field, TableId tableId, Reflector reflector) {
//        boolean underCamel = tableInfo.isUnderCamel();
//        final String property = field.getName();
//        if (field.getAnnotation(TableField.class) != null) {
//            logger.warn(String.format("This \"%s\" is the table primary key by @TableId annotation in Class: \"%s\",So @TableField annotation will not work!",
//                property, tableInfo.getEntityType().getName()));
//        }
//        /* 主键策略（ 注解 > 全局 ） */
//        // 设置 Sequence 其他策略无效
//        if (IdType.NONE == tableId.type()) {
//            tableInfo.setIdType(dbConfig.getIdType());
//        } else {
//            tableInfo.setIdType(tableId.type());
//        }
//
//        /* 字段 */
//        String column = property;
//        if (StringUtils.isNotBlank(tableId.value())) {
//            column = tableId.value();
//        } else {
//            // 开启字段下划线申明
//            if (underCamel) {
//                column = StringUtils.camelToUnderline(column);
//            }
//            // 全局大写命名
//            if (dbConfig.isCapitalMode()) {
//                column = column.toUpperCase();
//            }
//        }
//        tableInfo.setKeyRelated(checkRelated(underCamel, property, column))
//            .setKeyColumn(column)
//            .setKeyProperty(property)
//            .setKeyType(reflector.getGetterType(property));
//        return true;
//    }

//    /**
//     * <p>
//     * 主键属性初始化
//     * </p>
//     *
//     * @param tableInfo 表信息
//     * @param field     字段
//     * @param reflector Reflector
//     * @return true 继续下一个属性判断，返回 continue;
//     */
//    private static boolean initTableIdWithoutAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
//                                                        Field field, Reflector reflector) {
//        final String property = field.getName();
//        if (DEFAULT_ID_NAME.equalsIgnoreCase(property)) {
//            if (field.getAnnotation(TableField.class) != null) {
//                logger.warn(String.format("This \"%s\" is the table primary key by default name for `id` in Class: \"%s\",So @TableField will not work!",
//                    property, tableInfo.getEntityType().getName()));
//            }
//            String column = property;
//            if (dbConfig.isCapitalMode()) {
//                column = column.toUpperCase();
//            }
//            tableInfo.setKeyRelated(checkRelated(tableInfo.isUnderCamel(), property, column))
//                .setIdType(dbConfig.getIdType())
//                .setKeyColumn(column)
//                .setKeyProperty(property)
//                .setKeyType(reflector.getGetterType(property));
//            return true;
//        }
//        return false;
//    }

//    /**
//     * <p>
//     * 字段属性初始化
//     * </p>
//     *
//     * @param dbConfig  数据库全局配置
//     * @param tableInfo 表信息
//     * @param fieldList 字段列表
//     * @return true 继续下一个属性判断，返回 continue;
//     */
//    private static boolean initTableFieldWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
//                                                        List<TableFieldInfo> fieldList, Field field) {
//        /* 获取注解属性，自定义字段 */
//        TableField tableField = field.getAnnotation(TableField.class);
//        if (null == tableField) {
//            return false;
//        }
//        fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField));
//        return true;
//    }

    /**
     * <p>
     * 判定 related 的值
     * </p>
     *
     * @param underCamel 驼峰命名
     * @param property   属性名
     * @param column     字段名
     * @return related
     */
    public static boolean checkRelated(boolean underCamel, String property, String column) {
        if (StringUtils.isNotColumnName(column)) {
            // 首尾有转义符,手动在注解里设置了转义符,去除掉转义符
            column = column.substring(1, column.length() - 1);
        }
        String propertyUpper = property.toUpperCase(Locale.ENGLISH);
        String columnUpper = column.toUpperCase(Locale.ENGLISH);
        if (underCamel) {
            // 开启了驼峰并且 column 包含下划线
            return !(propertyUpper.equals(columnUpper) ||
                propertyUpper.equals(columnUpper.replace(StringPool.UNDERSCORE, StringPool.EMPTY)));
        } else {
            // 未开启驼峰,直接判断 property 是否与 column 相同(全大写)
            return !propertyUpper.equals(columnUpper);
        }
    }

    /**
     * <p>
     * 获取该类的所有属性列表
     * </p>
     *
     * @param clazz 反射类
     * @return 属性集合
     */
    public static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
        return fieldList.stream()
            .filter(field -> {
                /* 过滤注解非表字段属性 */
                Column column = field.getAnnotation(Column.class);
                return (column == null || column.name() != "");
            }).collect(toList());
    }

//    public static KeyGenerator genKeyGenerator(String baseStatementId, TableInfo tableInfo, MapperBuilderAssistant builderAssistant) {
//        IKeyGenerator keyGenerator = GlobalConfigUtils.getKeyGenerator(builderAssistant.getConfiguration());
//        if (null == keyGenerator) {
//            throw new IllegalArgumentException("not configure IKeyGenerator implementation class.");
//        }
//        Configuration configuration = builderAssistant.getConfiguration();
//        //TODO 这里不加上builderAssistant.getCurrentNamespace()的会导致com.baomidou.mybatisplus.core.parser.SqlParserHelper.getSqlParserInfo越(chu)界(gui)
//        String id = builderAssistant.getCurrentNamespace() + StringPool.DOT + baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
//        ResultMap resultMap = new ResultMap.Builder(builderAssistant.getConfiguration(), id, tableInfo.getKeyType(), new ArrayList<>()).build();
//        MappedStatement mappedStatement = new MappedStatement.Builder(builderAssistant.getConfiguration(), id,
//            new StaticSqlSource(configuration, keyGenerator.executeSql(tableInfo.getKeySequence().value())), SqlCommandType.SELECT)
//            .keyProperty(tableInfo.getKeyProperty())
//            .resultMaps(Collections.singletonList(resultMap))
//            .build();
//        configuration.addMappedStatement(mappedStatement);
//        return new SelectKeyGenerator(mappedStatement, true);
//    }
}
